function eval(t,s,o)
    local s = " "..s or " "
    local o = o or " "
    if t then
       return s
    end
    return o
end

check = eval

function ok()
    return true
end

function notok()
    return true
end

function cmd(msg)
   return " ["..msg.."]"
end

function give(t,o,n)
   assert(t,"Invalid target")
   assert(o,"Invalid object")
   local n = n or 1
   t[o] = (t[o] or 0) + n
   return "Gave "..n.." "..tostring(o).." to "..tostring(t)
end

--Function to fill replace all tokens within a string
function fillout(tokens,instring)
   local outstring = instring
   local k,v
   for k,v in pairs(tokens) do
       outstring = string.gsub(outstring, "$"..k, v)
   end
   return outstring
end

print(eval(ok(),"How are"..eval(ok(),"you?")))


listener = {

}
setmetatable(listener,{__tostring = function() return "Robert" end})
speaker = {}

function has(p,o,n)
  local n = n or 1
  if (p[o] or -1) >= n then --having an object means we have more or equal to n
     return true
  end
  return false
end

function hasnot(p,o,n)
  local n = n or 1
  if (p[o] or -1) < n then --not having an object means we have less than n
     return true
  end
  return false
end

print(fillout(
      {listenername = tostring(listener)},
      eval(
           has(listener,"bigknife"),
                                    "$listenername, I see you have a big knife",
                                    "$listenername, you don't have a knife, please take mine"
                                         ..cmd(give(listener,"bigknife"))
      )
     )    
)
     
print(eval(
           has(listener,"bigknife"),
                                    "$listener, I see you have a big knife",
                                    "You don't have a knife, please take mine"
                                         ..cmd(give(listener,"bigknife"))
      )
)     

print(eval(has(listener,"smallknife"),
                                    "$listener, I see you have a small knife",
                                    eval(has(listener,"bigknife"),
                                         "You don't have a small knife, but that big one will do just fine",
                                         "You don't have a knife, please take mine"
                                              ..cmd(give(listener,"smallknife"))
                                    )
      )
)
     
listener = {}

print(eval(has(listener,"smallknife"),
                                    "$listener, I see you have a small knife",
                                    eval(has(listener,"bigknife"),
                                         "You don't have a small knife, but that big one will do just fine",
                                         "You don't have a knife, please take mine"
                                              ..cmd(give(listener,"smallknife"))
                                    )
      )
)
--Add ability to include other dialog files, so we can make quest dialog files and
--have them included to character's default dialogs

--item slot and type checks
--arbitrary counters
--is check (belongs to class, is male, is human, is warrior)
--time check time(midnight)
--isnot,hasnot
--environment evironment(dark)
--cmd(script,areastate,give,take,count(counter),behave(anim/sound),camera)
--has x friends
--y is friend/enemy/knows x(person or faction)

dialog = [[
check(has(listener,"smallknife"),
         "$listener, I see you have a small knife",
         check(has(listener,"bigknife"),
              "You don't have a small knife, but that big one will do just fine",
              "You don't have a knife, please take mine"
                  ..cmd(give(listener,"smallknife"))
              )
     )
]]

function loaddialog(d)
   local d = "return function(environment,speaker,listener) return "..d.." end"
   return loadstring(d)()
end

dodialog = loaddialog(dialog)     
print(dodialog({},speaker,listener))

none = function()
     return none
end

environment =
{

}

race = { --class
  name   = "race",
  parent = none,
  onEventA = function() 
  end,
}

human = { --class
  name   = "human",
  parent = race,
  --classes inherit eventhandlers and override parent handlers
  onEventA = function(self) 
    parent:onEventA() --call parent event              
  end,
}

decorator = { 
  name = "hasshop",
  handlers = { --decorators are all equivalent and will all have their handlers called
  }
}

character = {
  id    = {
        meta  = "character",
        index = 1,
  },
  name  = "Robert",         --not unique
  title = "The Architect",  --not unique
  --name + title is unique
  classes    = {"human","agent"}, --is-a 
  decorators = {"hasshop"},       --has  
  stats = {--must add upto 10
        body=5,
        mind=3,
        soul=2,
  },
  items = {
     all = {--upto 32
     },
     worn = {
          head  = none,
          body  = none,
          lhand = none,
          rhand = none,
          feet  = none,
          accesories = {--upto 3
             none,none,none         
          }          
     },
     quicklist = {--upto 7 + accesories
          -- this needs to be a metamethod: accesories[1],accesories[2],accesories[3],
     },
  },
  dialog = none,   --default dialog, references a dialog file or inline dialog
  agent  = player, --who control's this
}

--players can create characters, collect items for them and stuff, write dialog for them and assign them an agent
--players can create an agent(bot) to control a character


--pets require a pet item and a trainer item (accesorie), player can have upto 3 active pets

client = {
   player = character,
   environment = environment,
   objects = {}, --all objects, including player
   characters = {}, --subset of objects
}

function intent(command,actor.id,target.id)
  --send to broker, where it is handled by my persona
end

persona = {
   server = {},
   client = {},
   commandhistory = { --circular buffer of recent commands
   
   }
} 

broker = {
  personas = {
    persona,
  }
}

--make a test CRUD layer in lua for the server

